package com.linghang.wusthelper.spider.jwc;

import com.linghang.wusthelper.common.URLConst;
import com.linghang.wusthelper.redis.RedisService;
import com.linghang.wusthelper.util.CodeIdentiUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.seimicrawler.xpath.JXDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

/**
 * @author origin
 * 教务处模拟登陆
 */
@Component
public class JwcSimLogin {

    private static Logger log = LoggerFactory.getLogger(JwcSimLogin.class);

    private static PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();

    private static RedisService redisService;

    static {
        connectionManager.setMaxTotal(100);
    }

    @Autowired
    private void setRedisService(RedisService redisService){JwcSimLogin.redisService = redisService;}

    /**
     * 模拟登陆
     * @param stuNum 学号
     * @param passwd 密码
     * @return resultMap  code:0 请求失败 ，1 请求成功，2 请求成功但密码或账号错误
     */
    public static Map<String,String> login(String stuNum,String passwd){
        Map<String,String> resultMap = new HashMap<>();
        try {
            //  检测缓存内是否有cookie
            String JSESSIONID = redisService.get(stuNum+":JSESSIONID");
            if (JSESSIONID != null){
                String SERVERID = redisService.get(stuNum+":SERVERID");
                long endTime = System.currentTimeMillis();
                //  检测缓存内cookie是否还有效
                if (testLogin(JSESSIONID,SERVERID)){
                    resultMap.put("JSESSIONID",JSESSIONID);
                    resultMap.put("SERVERID",SERVERID);
                    resultMap.put("code","1");
                    return resultMap;
                }else {
                    //  删除无效cookie
                    redisService.remove("JSESSIONID");
                    redisService.remove("SERVERID");
                }
            }
            Map<String,Object> map = downLodeCode();  //  下载验证码图片,返回cookie
            resultMap = simLogin(map,stuNum,passwd); //  返回登陆结果
            return resultMap;
        } catch (Exception e) {
            log.error(e.getMessage());
            log.error("教务处登陆失败:"+stuNum);
            resultMap.put("code","0");
            return resultMap;
        }
    }

    /**
     * 模拟教务处登陆
     * @param map cookie以及验证码
     * @param stuNum 学号
     * @param passwd 密码
     * @return resultMap code:0 请求失败 ，1 请求成功，2 请求成功但密码或账号错误
     * @throws IOException
     */
    private static Map<String,String> simLogin(Map<String,Object> map, String stuNum, String passwd) throws IOException, URISyntaxException {

        HttpPost httpPost = new HttpPost(URLConst.JWCLOGINURL);
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(5000).setSocketTimeout(5000).setConnectTimeout(5000).build();
        httpPost.setConfig(requestConfig);

        CookieStore cookieStore = (CookieStore) map.get("cookieStore");      //从map中获取cookie

        //  将cookie加入请求头
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultCookieStore(cookieStore).build();

        //  设置请求头
        httpPost.setHeader("Connection", "keep-alive");
        httpPost.setHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        httpPost.setHeader("Accept-Encoding", "gzip,deflate,sdch");
        httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.8");
        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36");
        httpPost.setHeader("Cache-Control","no-cache");
        httpPost.setHeader("Host","jwxt.wust.edu.cn");

        //  添加登陆参数
        List<NameValuePair> pairs = new ArrayList<>();
        pairs.add(new BasicNameValuePair("USERNAME",stuNum));
        pairs.add(new BasicNameValuePair("PASSWORD",passwd));
        pairs.add(new BasicNameValuePair("useDogCode",""));
        pairs.add(new BasicNameValuePair("RANDOMCODE", (String) map.get("code")));
        pairs.add(new BasicNameValuePair("x","47"));
        pairs.add(new BasicNameValuePair("y","15"));
        httpPost.setEntity(new UrlEncodedFormEntity(pairs,"UTF-8"));

        CloseableHttpResponse httpResponse = httpClient.execute(httpPost);  //  执行请求

        Map<String,String> resultMap = new HashMap<>();
        //  判断网络请求是否成功
        if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){

            //  获取错误信息
            HttpEntity entity = httpResponse.getEntity();
            String result = EntityUtils.toString(entity,"UTF-8");
            JXDocument jxDocument = JXDocument.create(result);
            String xpth = "//*[@id=\"errorinfo\"]/text()";
            String errorMsg = (String) jxDocument.selOne(xpth);

            //  判断是否登录成功
            if (errorMsg != null){
                resultMap.put("code","2");
                resultMap.put("errorMsg",errorMsg);
                httpPost.releaseConnection();
                httpResponse.close();
                return resultMap;
            } else {
                //  获取cookie
                List<Cookie> cookies = cookieStore.getCookies();
                for (Cookie tmp : cookies){
                    if (tmp.getName().equals("JSESSIONID")){
                        resultMap.put("JSESSIONID",tmp.getValue());
                    }
                    if (tmp.getName().equals("SERVERID")){
                        resultMap.put("SERVERID",tmp.getValue());
                    }
                }

                //  再访问一次主页，教务处认证(不访问无法进行其他操作)
                httpPost.setURI(new URI(URLConst.IDENTIFICATEURL));
                httpClient.execute(httpPost);

                // 将cookie存入缓存
                redisService.set(stuNum+":JSESSIONID",resultMap.get("JSESSIONID"),1200);
                redisService.set(stuNum+":SERVERID",resultMap.get("SERVERID"),1200);

                resultMap.put("code","1");  //  登录成功
                httpPost.releaseConnection();
                httpResponse.close();
                httpPost.releaseConnection();
                httpResponse.close();
                return resultMap;
            }
        }else {
            resultMap.put("code","0");  //  网络请求失败
            httpPost.releaseConnection();
            httpResponse.close();
            return resultMap;
        }
    }

    /**
     * 下载验证码图片
     * @return cookie以及验证码
     * @throws IOException
     */
    private static Map<String,Object> downLodeCode() throws IOException {

        HttpGet httpGet = new HttpGet(URLConst.JWCCODEURL);
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(5000).setSocketTimeout(5000).setConnectTimeout(5000).build();
        httpGet.setConfig(requestConfig);
        CookieStore cookieStore = new BasicCookieStore();
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultCookieStore(cookieStore).build();


        //  设置请求头
        httpGet.setHeader("Connection", "keep-alive");
        httpGet.setHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        httpGet.setHeader("Accept-Encoding", "gzip,deflate,sdch");
        httpGet.setHeader("Accept-Language", "zh-CN,zh;q=0.8");
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36");


        String code = "";
        CloseableHttpResponse httpResponse = httpClient.execute(httpGet);  //  执行请求

        //  判断网络请求是否成功
        if (HttpStatus.SC_OK == httpResponse.getStatusLine().getStatusCode()){
            HttpEntity httpEntity = httpResponse.getEntity();
            InputStream in = httpEntity.getContent();
            String uuid = saveCodePic(in);  //  下载验证码图片，返回图片文件UUID
            code = CodeIdentiUtil.CodeIdentification(uuid);
        }
        Map<String,Object> map = new HashMap<>();
        map.put("code",code);
//        System.out.println(code);
        map.put("cookieStore",cookieStore);
//        System.out.println(cookieStore);
        httpGet.releaseConnection();
        httpResponse.close();
        return map;
    }

    /**
     * 保存验证码图片
     * @param in 图片输入流
     * @return 图片存储地址
     */
    private static String saveCodePic(InputStream in){
        //todo  设置验证码存储路径
        String dirTh = "./";
        //  生成图片UUID,用于文件名
        String uuid = UUID.randomUUID().toString();
        File file = new File(dirTh+uuid+".png");

        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = in.read(buffer)) != -1){
                fileOutputStream.write(buffer,0,len);
            }
            fileOutputStream.flush();
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return uuid;
    }

    /**
     * 检测缓存内的cookie是否还有效
     * @param JSESSIONID
     * @param SERVERID
     * @return
     * @throws IOException
     */
    private static boolean testLogin(String JSESSIONID,String SERVERID) throws IOException {



        HttpGet httpGet = new HttpGet(URLConst.IDENTIFICATEURL);
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(5000).setSocketTimeout(5000).setConnectTimeout(5000).build();
        httpGet.setConfig(requestConfig);

        CookieStore cookieStore = new BasicCookieStore();

        //  添加cookie
        BasicClientCookie cookie1 = new BasicClientCookie("JSESSIONID",JSESSIONID);
        cookie1.setDomain("jwxt.wust.edu.cn");
        cookie1.setVersion(0);
        cookie1.setPath("/");
        BasicClientCookie cookie2 = new BasicClientCookie("SERVERID",SERVERID);
        cookie2.setDomain("jwxt.wust.edu.cn");
        cookie2.setVersion(0);
        cookie2.setPath("/");
        cookieStore.addCookie(cookie1);
        cookieStore.addCookie(cookie2);

        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultCookieStore(cookieStore).build();

        //  设置请求头
//        httpGet.setHeader("Connection", "keep-alive");
        httpGet.setHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        httpGet.setHeader("Accept-Encoding", "gzip,deflate,sdch");
        httpGet.setHeader("Accept-Language", "zh-CN,zh;q=0.8");
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36");

        CloseableHttpResponse httpResponse = httpClient.execute(httpGet);  //  执行请

        HttpEntity entity = httpResponse.getEntity();
        String result = EntityUtils.toString(entity,"UTF-8");     //  获取html页面
        String head = result.substring(0,5);
        httpGet.releaseConnection();
        httpResponse.close();
        if (head != null && head.equals("<?xml")){
            return true;
        }else {
            return false;
        }
    }

}
